/*
 * module_flash.c
 *
 *  Created on: Jun 13, 2010
 *      Author: jmonk
 */

#include "mox.h"
#include "mox_module.h"
#include <mox_threads.h>
#include <mox_storage.h>
#include <mox_log.h>
#include "module_flash.h"
#include "spi_flash.h"
#include "spi_master.h"
#include "at25.h"
#include <print.h>
#include <xccompat.h>
#include <xs1_g4000b-512.h>
#include <xs1_user.h>
#include <stdlib.h>

void module_start_flash(void)
{
	chanend mox;
	int tmp, active;
	spi_master_interface spi_if;
	port spi_ss = XS1_PORT_1B;
	mox_msg *msg;
	unsigned thd_id;
	unsigned info[] = { TH_TYPE_MOD, MOD_STORAGE, 1};
	mox_msg thd_info = {
			TH_PARAM,
			3*sizeof(unsigned),
			(unsigned char *)info
	};

	THREAD_START_GET_CHAN(mox);

	thd_id = thd_std_start(mox, thd_info);

	spi_if.blk1 = XS1_CLKBLK_1;
	spi_if.blk2 = XS1_CLKBLK_2;
	spi_if.mosi = XS1_PORT_1D;
	spi_if.sclk = XS1_PORT_1C;
	spi_if.miso = XS1_PORT_1A;

	RES_IN_USE(spi_if.blk1);
	RES_IN_USE(spi_if.blk2);
	RES_IN_USE(spi_ss);
	PORT_USE_BUFFERED(spi_if.mosi, 8);
	PORT_USE_BUFFERED(spi_if.sclk, 8);
	PORT_USE_BUFFERED(spi_if.miso, 8);

	flash_init(spi_ss, &spi_if);

	printstrln("Starting Main Storage Service Loop");

	active = 0;
	while (1) {
		//tmp = mox_chan_in_wd(mox);
		//if (tmp == MOX_MSG) {
			msg = thd_comm_msg_recv(mox);
			switch (msg && msg->type) {
			case STORAGE_FREE:
				if (active) msg->type = STORAGE_BUSY;
				else msg->type = STORAGE_FREE;
				thd_comm_msg_send(mox, *msg);
				free(msg);
				break;
			case STORAGE_LOCK:
				if (!active) {
					active = 1;
					msg->type = STORAGE_ACK;
				} else {
					msg->type = STORAGE_NACK;
				}
				thd_comm_msg_send(mox, *msg);
				free(msg);
				break;
			case STORAGE_UNLK:
				if (active) {
					active = 0;
					msg->type = STORAGE_ACK;
				} else {
					msg->type = STORAGE_NACK;
				}
				thd_comm_msg_send(mox, *msg);
				free(msg);
				break;
			case STORAGE_READ:
				if (!active) {
					module_log(mox, "Warning: module used when not locked, please request lock before reading or writing\n");
				}
				flash_handle_read(mox, spi_ss, &spi_if, msg);
				free(msg);
				break;
			case STORAGE_WRTE:
				if (!active) {
					module_log(mox, "Warning: module used when not locked, please request lock before reading or writing\n");
				}
				flash_handle_write(mox, spi_ss, &spi_if, msg);
				free(msg);
				break;
			case STORAGE_CLR:
				if (!active) {
					module_log(mox, "Warning: module used when not locked, please request lock before reading or writing\n");
				}
				flash_handle_clear(mox, spi_ss, &spi_if, msg);
				free(msg);
				break;
			default:
				msg->type = STORAGE_NACK;
				thd_comm_msg_send(mox, *msg);
				free(msg);

				module_log(mox, "Unexpected storage signal");
				break;
			}
		//} else if ((tmp & MSG_TYPE) == MOD_MSG) {
		//	mox_chan_out_wd(mox, MOD_READY);
		//} else if ((tmp & MSG_TYPE) == SIG_MSG) {
		//	if (tmp == SIG_KILL) {
		//		flash_shutdown(&spi_if);
		//		mox_chan_out_wd(mox, SIG_KILL);
		//		thread_exit();
		//	} else {
		//		mox_chan_out_wd(mox, SIG_ACK);
		//	}
		//}

	}
}

int flash_handle_clear(chanend mox, port spi_ss, spi_master_interface *spi_if, mox_msg *msg)
{
	unsigned i;
	unsigned addr;

	for (i = 0; i < msg->size / sizeof(unsigned); i++) {
		addr = ((unsigned *)msg->content)[i];
		flash_clear(spi_ss, spi_if, (int *)&addr, 1, mox);
	}

	msg->type = STORAGE_ACK;

	thd_comm_msg_send(mox, *msg);
	m_free(mox, msg->content);
	m_free(mox, msg);

	return 0;
}


int flash_handle_write(chanend mox, port spi_ss, spi_master_interface *spi_if, mox_msg *msg)
{
	unsigned input;
	unsigned addr;
	signed size;
	unsigned char *buf;
	int i;

	addr = ((unsigned *)msg->content)[0];
	msg->type = STORAGE_ACK;
	thd_comm_msg_send(mox, *msg);
	m_free(mox, msg->content);
	m_free(mox, msg);

	msg = thd_comm_msg_recv(mox);
	size = msg->size;
	buf = msg->content;
	while (size > 0) {
		if (size >= 256)
			flash_write(spi_ss, spi_if, buf, 256, (int *)&addr);
		else
			flash_write(spi_ss, spi_if, buf, size, (int *)&addr);

		size -= 256;
		buf += 256;
		asm("nop");
		asm("nop");
	}
	m_free(mox, buf);
	msg->content = NULL;
	msg->size = 0;
	msg->type = STORAGE_ACK;

	thd_comm_msg_send(mox, *msg);

	m_free(mox, msg);

	return 0;
}

int flash_handle_read(chanend mox, port spi_ss, spi_master_interface *spi_if, mox_msg *msg)
{
	unsigned addr;
	unsigned size;
	unsigned *data;
	unsigned i;

	addr = ((unsigned *)msg->content)[0];
	size = ((unsigned *)msg->content)[1];
	//if (size&0x03) size += (4 - size&0x03);
	data = (unsigned *)m_malloc(mox, size);
	if (!data) {
		msg->type = STORAGE_NACK;
		thd_comm_msg_send(mox, *msg);
		return 1;
	}

	msg->type = STORAGE_ACK;
	m_free(mox, msg->content);
	flash_read(spi_ss, spi_if, (unsigned char *)data, addr, size);
	msg->content = (unsigned char *)data;
	msg->size = size;
	thd_comm_msg_send(mox, *msg);
	m_free(mox, data);
	m_free(mox, msg);

	return 0;
}


/*
 * wait_for_device()
 * Does not return until the device is not busy.
 */
void wait_for_device_or_os(port spi_ss, spi_master_interface *spi_if, chanend mox)
{
	signed tmp;
	spi_select(spi_ss);
	spi_out_byte(spi_if, read_status_reg);

	while (spi_in_byte(spi_if) & 0x01) {
		asm("nop");			// op nop nop
		asm("nop");
		tmp = mox_chan_in_noblock(mox);
		if ((tmp & MSG_TYPE) == STORAGE_MSG) {
			mox_chan_out_wd(mox, STORAGE_WAIT);
		} else if ((tmp & MSG_TYPE) == SIG_MSG) {
			mox_chan_out_wd(mox, SIG_NACK);
		} else if (tmp != -1){
			mox_chan_out_wd(mox, MOD_BUSY);
		}
	}
	spi_deselect(spi_ss);
}
